package org.simtube.index;

import java.util.Iterator;
import java.util.LinkedList;

public class CNode {
	
	// fields
	
	private CNodeType type;
	private LinkedList<CEntry> elist;
	private CEntry entry;
	
	// setters and getters
	
	public void setType(CNodeType type) {
		this.type = type;
	}
	public CNodeType getType() {
		return type;
	}
	public void setElist(LinkedList<CEntry> elist) {
		this.elist = elist;
	}
	public LinkedList<CEntry> getElist() {
		return elist;
	}
	public void setEntry(CEntry entry) {
		this.entry = entry;
	}
	public CEntry getEntry() {
		return entry;
	}
	public int size() {
		return (elist == null ? 0 : elist.size());
	}
	
	
	// main methods
	
	public CNode(CNodeType type, CEntry entry) {
		this.setType(type);
		this.setEntry(entry);
		elist = new LinkedList<CEntry>();
	}
	
	/**
	 * Split a node when it is full. Some of the entries in the original node
	 * will be moved to a newly created node. 
	 * 
	 * @param The minimal number of entries that a node should have
	 * @return A new node created.
	 */
	public CNode split(int m) {
		
		CEntry[] seeds = getSeeds();
		
		CEntry min = seeds[0];
		CEntry max = seeds[1];
		
		CNode nn = new CNode(type, null);
		
		Iterator<CEntry> it = elist.iterator();
		CEntry en = null;
		
		nn.getElist().add(max);
		
		int nmax = 1;
		int nmin = 0;
		
		while(it.hasNext()) {
			en = it.next();
			
			if(nmax < m && (Region.distance(min.getRegion(), en.getRegion())
					> Region.distance(max.getRegion(), en.getRegion())
					|| nmin >= m) && en != max) {
				nmax++;
				en.setCurrentNode(nn);
				nn.getElist().add(en);
			} else if(en != max) {
				nmin++;
			}
		}
		
		it = nn.getElist().iterator();
		while(it.hasNext()) elist.remove(it.next());
		
		System.out.println("Split node A: " + this.size());
		System.out.println("Split node B: " + nn.size());
		
		return nn;
	}
	
	/**
	 * Get seed entries to split
	 * 
	 * @return Two seeds in an entry array
	 */
	private CEntry[] getSeeds() {
		
		CEntry emin = null;
		CEntry emax = null;
		double min = Double.POSITIVE_INFINITY;
		
		Iterator<CEntry> it = elist.iterator();
		CEntry ce = null;
		double temp = 0;
		
		while(it.hasNext()) {
			ce = it.next();
			temp = ce.getRegion().dimSum();
			if(temp < min) {
				min = temp;
				emin = ce;
			}
		}
		
		double max = Double.NEGATIVE_INFINITY;
		it = elist.descendingIterator();
		while(it.hasNext()) {
			ce = it.next();
			// System.out.println("Dim sum: " + ce.getRegion().dimSum());
			temp = ce.getRegion().dimSum();
			if(temp > max) {
				max = temp;
				emax = ce;
			}
		}
		
		if(emin == emax) {
			System.err.println("Split failed! Only one seed is picked...");
		}
		if(emin == null) {
			System.err.println("Failed to find a min seed...");
		}
		if(emax == null) {
			System.err.println("Failed to find a max seed...");		
		}
		
		CEntry[] retEntry = new CEntry[2];
		retEntry[0] = emin;
		retEntry[1] = emax;
		
		return retEntry;
	}
	
	/**
	 * Create an entry to cover this node
	 * 
	 * @return The created entry
	 */
	public CEntry createEntry() {
		// System.out.println(this.size());
		Region[] rlist = new Region[this.size()];
		Iterator<CEntry> it = elist.iterator();
		int i = 0;
		while(it.hasNext()) {
			rlist[i++] = it.next().getRegion();
		}
		
		return new CEntry(Region.minCoverRegion(rlist), this);
	}
}
